#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h> // 网络四件套
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <pthread.h>
#include <memory>
#include "log.hpp"
#include "threadPool.hpp"
#include "Task.hpp"
#include <unordered_map>

// static void service(int sock, const std::string &clientip, const uint16_t &clientport)
// {
//     //echo server
//     char buffer[1024];
//     while(true)
//     {
//         // read && write 可以直接被使用
//         ssize_t s = read(sock, buffer, sizeof(buffer)-1);
//         if(s > 0)
//         {
//             buffer[s] = 0; // 将发过来的数据当做字符串
//             std::cout << clientip << ":" << clientport << "# " << buffer << std::endl;
//         }
//         else if(s == 0) // 对端关闭连接
//         {
//             logMessage(NORMAL, "%s:%d shutdown, me too", clientip.c_str(), clientport);
//             break;
//         }
//         else
//         {
//             logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));
//             break;
//         }
//         write(sock, buffer, strlen(buffer));
//     }
// }

static void service(int sock, const std::string &clientip, 
                                const uint16_t &clientport, const std::string &thread_name) // 带上线程名的service
{
    // echo server
    char buffer[1024];
    while (true)
    {
        // read && write 可以直接被使用
        ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0; // 将发过来的数据当做字符串
            std::cout << thread_name << "|" << clientip << ":" << clientport << "# " << buffer << std::endl;
        }
        else if (s == 0) // 对端关闭连接
        {
            logMessage(NORMAL, "%s:%d shutdown, me too", clientip.c_str(), clientport);
            break;
        }
        else
        {
            logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));
            break;
        }
        write(sock, buffer, strlen(buffer));
    }
    close(sock);
}

static void change(int sock, const std::string &clientip,
                   const uint16_t &clientport, const std::string &thread_name)
{
    //  一般服务器进程业务处理，如果是从连上，到断开，要一直保持这个链接, 长连接
    char buffer[1024];
    // read && write 可以直接被使用
    ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
    if (s > 0)
    {
        buffer[s] = 0; // 将发过来的数据当做字符串
        std::cout << thread_name << "|" << clientip << ":" << clientport << "# " << buffer << std::endl;
        std::string message;
        char *start = buffer;
        while(*start)
        {
            char c;
            if(islower(*start)) 
            {
                c = toupper(*start);
            }
            else
            {
                c = *start;
            }
            message.push_back(c);
            ++start;
        }
        write(sock, message.c_str(), message.size());
    }
    else if (s == 0) // 对端关闭连接
    {
        logMessage(NORMAL, "%s:%d shutdown, me too", clientip.c_str(), clientport);
    }
    else
    {
        logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));
    }
    close(sock);
}

static void dictOnline(int sock, const std::string &clientip,
                   const uint16_t &clientport, const std::string &thread_name)
{
    //  一般服务器进程业务处理，如果是从连上，到断开，要一直保持这个链接, 长连接
    char buffer[1024];
    static std::unordered_map<std::string, std::string> dict = {
        {"apple", "苹果"},
        {"watermelon", "西瓜"},
        {"banana", "香蕉"},
        {"hard", "好难"}
    };
    // read && write 可以直接被使用
    ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
    if (s > 0)
    {
        buffer[s] = 0; // 将发过来的数据当做字符串
        std::cout << thread_name << "|" << clientip << ":" << clientport << "# " << buffer << std::endl;
        std::string message;
        auto iter = dict.find(buffer);
        if(iter == dict.end()) 
            message = "此字典找不到...";
        else 
            message = iter->second;
        write(sock, message.c_str(), message.size());
    }
    else if (s == 0) // 对端关闭连接
    {
        logMessage(NORMAL, "%s:%d shutdown, me too", clientip.c_str(), clientport);
    }
    else
    {
        logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));
    }

    close(sock);
}

// class ThreadData
// {
// public:
//     int _sock;
//     std::string _ip;
//     uint16_t _port;
// };

class TcpServer
{
protected:
    const static int gbacklog = 7;  // listen的第二个参数，现在先不管

    // static void *threadRoutine(void *args) // 加上static就没this指针了
    // {
    //     pthread_detach(pthread_self()); // 线程分离
    //     ThreadData *td = static_cast<ThreadData *>(args);
    //     service(td->_sock, td->_ip, td->_port);
    //     delete td;

    //     return nullptr;
    // }
public:
    TcpServer(uint16_t port, std::string ip="")
        :_listensock(-1)
        , _port(port)
        , _ip(ip)
        , _threadpool_ptr(ThreadPool<Task>::getThreadPool())
    {}
    void initServer()
    {
        // 1. 创建socket -- 进程和文件
        _listensock = socket(AF_INET, SOCK_STREAM, 0); // 域 + 类型 + 0 // UDP第二个参数是SOCK_DGRAM
        if(_listensock < 0)
        {
            logMessage(FATAL, "create socket error, %d:%s", errno, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "create socket success, _listensock: %d", _listensock);

        // 2. bind -- 文件 + 网络
        struct sockaddr_in local;
        memset(&local, 0, sizeof local);
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        if(bind(_listensock, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "bind error, %d:%s", errno, strerror(errno));
            exit(3);
        }

        // 3. 因为TCP是面向连接的，当正式通信的时候，需要先建立连接，UDP没这一步
        if(listen(_listensock, gbacklog) < 0) // 第一个参数是套接字，第二个参数后面再说
        {
            logMessage(FATAL, "listen error, %d:%s", errno, strerror(errno));
            exit(4);
        }

        logMessage(NORMAL, "init server success");
    }

    void start()
    {
        // 下面父进程的阻塞式等待 -> 用信号代替 -> 子进程退出会像父进程发送SIGCHLD信号
        // signal(SIGCHLD, SIG_IGN); // 对SIGCHLD,主动忽略SIGCHLD信号,子进程退出的时候,会自动释放自己的僵尸状态
        _threadpool_ptr->run();
        while(true)
        {
            // sleep(1);
            // 4. 获取连接
            struct sockaddr_in src;
            socklen_t len = sizeof(src);
            int servicesock = accept(_listensock, (struct sockaddr*)&src, &len); // accept获取新链接
            // servicesock(服务套接字，相当于此小区域专门给你负责的) 
            // 对比 类内的_sock(类似于小区域外面拉客的，拉来小区域就交给servicesock管了)
            if(servicesock < 0)
            {
                logMessage(ERROR, "accept error, %d:%s", errno, strerror(errno));
                continue;
            } 
            // 获取连接成功了
            uint16_t client_port = ntohs(src.sin_port);
            std::string client_ip = inet_ntoa(src.sin_addr);
            logMessage(NORMAL, "link success, servicesock: %d | %s : %d |\n",\
                 /*换行符*/servicesock, client_ip.c_str(), client_port);
            // 开始进行通信服务

            // // version 1 -- 单进程循环版 -- 只能够进行一次处理一个客户端，处理完了一个，才能处理下一个
            // // 很显然，是不能够直接被使用的 -- 为什么？ -> 要从单进程改成多线程
            // service(servicesock, client_ip, client_port);


            // // 2 version 2.0 -- 多进程版 --- 创建子进程
            // // 让子进程给新的连接提供服务，子进程能不能打开父进程曾经打开的文件fd呢？ -> 能
            // pid_t id = fork();
            // assert(id != -1);
            // if(id == 0) // 子进程
            // {
            //     // 子进程会不会继承父进程打开的文件与文件fd呢？ -> 会
            //     // 子进程是来进行提供服务的，需不需要知道监听socket呢？ -> 不需要
            //     close(_listensock); // 关闭自己不需要的套接字
            //     service(servicesock, client_ip, client_port); // 子进程对新链接提供服务
            //     exit(0); // 僵尸状态
            // }
            // // 父进程
            // close(servicesock); // 父进程关闭自己不需要的套接字，不关的话文件描述符会越少
            // // 如果父进程关闭servicesock，会不会影响子进程?

            // // 前面有了signal(SIGCHLD, SIG_IGN); -> 父进程不用等了
            // // waitpid(); // 阻塞式等待 -> 用信号代替 -> 子进程退出会像父进程发送SIGCHLD信号


            // // version2.1 -- 多进程版 -- version 2.0 改版
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     // 子进程
            //     close(_listensock);
            //     if(fork() > 0)  // 再创建子进程，子进程本身
            //         exit(0); //子进程本身立即退出
            //     // 到这是(孙子进程)，是孤儿进程，被OS领养，OS在孤儿进程退出的时候，由OS自动回收孤儿进程
            //     service(servicesock, client_ip, client_port);
            //     exit(0);
            // }
            // // 父进程
            // waitpid(id, nullptr, 0); // 不会阻塞
            // close(servicesock);

            // // version 3 --- 多线程版本
            // // 创建进程的成本是很高的，所以再改成多线程版(不封装了)
            // ThreadData *td = new ThreadData();
            // td->_sock = servicesock;
            // td->_ip = client_ip;
            // td->_port = client_port;
            // pthread_t tid;
            // // 在多线程这里不用(不能)进程关闭特定的文件描述符，因为是共享的
            // pthread_create(&tid, nullptr, threadRoutine, td); // 到这里写threadRoutine再写ThreadData类

            // verison4 --- 线程池版本
            // Task t(servicesock, client_ip, client_port, service);
            // Task t(servicesock, client_ip, client_port, change);
            Task t(servicesock, client_ip, client_port, dictOnline);
            _threadpool_ptr->pushTask(t);
        }
    }

    ~TcpServer()
    {}
protected:
    uint16_t _port;
    std::string _ip;
    int _listensock;
    std::unique_ptr<ThreadPool<Task>> _threadpool_ptr;
};